package cn.xiaohello.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * NIO 通信服务器端程序,主要参考资料如下 {@link}http://weixiaolu.iteye.com/blog/1479656
 * 
 * @author XIAO
 *
 */
public class NIOServerTest {
	// 通道管理器
	private Selector selector = null;

	/**
	 * 获得一个serversocket 通道，并为该通道做初始化工作
	 * 
	 * @param port
	 * @throws IOException
	 */
	public void initServer(int port) throws IOException {
		// 打开一个serverSocket通道
		ServerSocketChannel ssc = ServerSocketChannel.open();
		// 设置通道为非阻塞
		ssc.configureBlocking(false);
		// 将通道对应的ServerSocket绑定到对应的端口（监听端口）
		ssc.socket().bind(new InetSocketAddress(port));
		// 获得一个通道管理器
		this.selector = Selector.open();
		// 将通道和通道管理器绑定，并为通道注册 SelectionKey.OP_ACCEPT事件
		// 注册该事件后，当事件到达时，selector.select() 会返回，如果没有事件，则阻塞
		ssc.register(selector, SelectionKey.OP_ACCEPT);
	}

	/**
	 * 采用轮询方式监听 selector上是否有需要处理的事件
	 * @throws IOException
	 */
	public void listen() throws IOException {
		System.out.println("服务器准备完毕。。。");
		// 轮询访问selector
		while (true) {
			// 当注册事件发生时，该方法会返回，否则一直阻塞
			this.selector.select();
			// 获取selector中选中项的迭代器，
			Iterator<SelectionKey> iter = this.selector.selectedKeys()
					.iterator();
			while (iter.hasNext()) {
				SelectionKey key = iter.next();
				// 删除已选的key，防止重复处理
				iter.remove();
				// 客户端请求事件
				if (key.isAcceptable()) {
					// 获得事件响应的服务器通道
					ServerSocketChannel server = (ServerSocketChannel) key
							.channel();
					// 获得与客户端链接的通道
					SocketChannel channel = server.accept();
					// 把通道设置成非阻塞
					channel.configureBlocking(false);
					// 向客户端发送消息
					channel.write(ByteBuffer.wrap(("hello, i am server")
							.getBytes()));

					// 在和客户端连接成功之后，为了可以接收到客户端的信息，需要给通道设置读的权限
					channel.register(this.selector, SelectionKey.OP_READ);

					// 获得了可读事件
				} else if (key.isReadable()) {
					read(key);
				}
			}
		}
	}

	/** 
     * 处理读取客户端发来的信息 的事件 
     * @param key 
     * @throws IOException  
     */  
    public void read(SelectionKey key) throws IOException{  
        // 服务器可读取消息:得到事件发生的Socket通道  
        SocketChannel channel = (SocketChannel) key.channel();  
        // 创建读取的缓冲区  
        ByteBuffer buffer = ByteBuffer.allocate(1024);  
        channel.read(buffer);  
        byte[] data = buffer.array();  
        String msg = new String(data).trim();  
        System.out.println("服务端收到信息："+msg);  
        ByteBuffer outBuffer = ByteBuffer.wrap("server have receive client msg".getBytes());  
        channel.write(outBuffer);// 将消息回送给客户端  
    }  

	public static void main(String[] args) throws IOException {
		NIOServerTest server = new NIOServerTest();  
        server.initServer(80);  
        server.listen();  
	}
}
